package com.ktkj.init.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ktkj.init.constant.DictConstant;
import com.ktkj.init.dto.DictCondition;
import com.ktkj.init.dto.DictForm;
import com.ktkj.init.dto.DictListTreeCondition;
import com.ktkj.init.entity.Dict;
import com.ktkj.init.exception.BusinessException;
import com.ktkj.init.mapper.CommonMapper;
import com.ktkj.init.mapper.DictMapper;
import com.ktkj.init.mapper.vo.TableData;
import com.ktkj.init.mapper.vo.TableMap;
import com.ktkj.init.service.DictService;
import com.ktkj.init.service.UserInfoService;
import com.ktkj.init.utils.DateUtils;
import com.ktkj.init.utils.ExcelUtils;
import com.ktkj.init.utils.IdentitiesUtils;
import com.ktkj.init.utils.SpringUtils;
import com.ktkj.init.vo.DictExport;
import com.ktkj.init.vo.QueryResult;
import com.ktkj.init.vo.TreeDataVo;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 * 字典表 服务实现类
 * </p>
 *
 * @author yanlixiong
 * @since 2020-09-23
 */
@Service
public class DictServiceImpl extends ServiceImpl<DictMapper, Dict> implements DictService {

    @Resource
    private CommonMapper commonMapper;

    @Resource
    private UserInfoService userInfoService;

    @Override
    public QueryResult<Dict> page(DictCondition condition) {
        QueryWrapper<Dict> wrapper = new QueryWrapper<>();
        if (StringUtils.isNotBlank(condition.getDescs())) {
            wrapper.like("descs", condition.getDescs().trim());
        }
        if (StringUtils.isNotBlank(condition.getType())) {
            wrapper.eq("type", condition.getType().trim());
        }
        if (condition.getParentId() != null) {
            wrapper.eq("parent_id", condition.getParentId());
        }
        if (StringUtils.isNotBlank(condition.getManageType())) {
            wrapper.eq("manage_type", condition.getManageType().trim());
        }
        wrapper.orderByAsc("level");

        List<Dict> resultData;
        long totalRecord;
        if (condition.getCurrent() != -1) {
            IPage<Dict> page = page(new Page<>(condition.getCurrent(), condition.getPageSize()), wrapper);
            resultData = page.getRecords();
            totalRecord = page.getTotal();
        } else {
            resultData = list(wrapper);
            totalRecord = resultData.size();
        }
        if (!CollectionUtils.isEmpty(resultData)) {
            for (Dict dict : resultData) {
                getParents(dict);
            }
        }
        QueryResult<Dict> queryResult = new QueryResult<>();
        queryResult.setResultData(resultData);
        queryResult.setTotalRecord(totalRecord);
        return queryResult;
    }

    @Override
    public List<Dict> getDictDir() {
        QueryWrapper<Dict> wrapper = new QueryWrapper<>();
        wrapper.eq("manage_type", DictConstant.MANAGE_TYPE_DIR);
        wrapper.eq("parent_id", DictConstant.HIGHEST_CODE);
        wrapper.orderByAsc("level");
        List<Dict> list = list(wrapper);
        for (Dict dict : list) {
            getChildrenList(dict, DictConstant.MANAGE_TYPE_DATA);
        }
        return list;
    }


    @Override
    public List<Dict> getByTypeAndPId(DictCondition condition) {
        condition.setCurrent(-1);
        QueryResult<Dict> page = this.page(condition);
        return page.getResultData();
    }

    @Override
    public Dict getByTypeAndDescsAndPId(DictCondition condition) {
        Dict dict = getOne(new QueryWrapper<Dict>()
                .lambda()
                .eq(Dict::getType, condition.getType().trim())
                .eq(Dict::getDescs, condition.getDescs().trim())
                .eq(Dict::getParentId, condition.getParentId()));
        if (dict == null) {
            dict = this.saveOrUpdate(new DictForm(condition.getParentId(), condition.getDescs(), condition.getType()));
        }
        return dict;
    }

    @Override
    public Dict getByDescsAndPId(DictCondition condition) {
        return getOne(new QueryWrapper<Dict>()
                .lambda()
                .eq(Dict::getDescs, condition.getDescs().trim())
                .eq(Dict::getParentId, condition.getParentId())
        );
    }


    @Override
    public Dict getChildrenList(Dict dict, String manageType) {
        Long id = dict.getId();
        QueryWrapper<Dict> wrapper = new QueryWrapper<>();
        wrapper.eq("parent_id", id);
        if (StringUtils.isNotBlank(manageType)) {
            wrapper.eq("manage_type", manageType);
        }
        List<Dict> dataList = list(wrapper);
        if (!CollectionUtils.isEmpty(dataList)) {
            dict.setChildrenList(dataList);
            for (Dict bean : dataList) {
                getChildrenList(bean, manageType);
            }
        }
        return dict;
    }


    @Override
    public Dict getParents(Dict dict) {
        Dict parent = getById(dict.getParentId());
        if (parent != null) {
            dict.setParent(parent);
            getParents(parent);
        }
        return dict;
    }

    @Override
    public Dict getData(String descs, String type) {
        Dict dict = getOne(new QueryWrapper<Dict>()
                .lambda()
                .eq(Dict::getDescs, descs.trim())
                .eq(Dict::getType, type.trim())
                .eq(Dict::getManageType, DictConstant.MANAGE_TYPE_DATA)
        );
        if (dict == null) {
            Dict parent = getOne(new QueryWrapper<Dict>()
                    .lambda()
                    .eq(Dict::getType, type.trim())
                    .eq(Dict::getManageType, DictConstant.MANAGE_TYPE_DIR)
            );
            if (parent != null) {
                dict = this.saveOrUpdate(new DictForm(parent.getId(), descs, type));
            }
        }
        return dict;
    }

    @Override
    public Dict getByDescs(String descs) {
        Dict dict = getOne(new QueryWrapper<Dict>()
                .lambda()
                .eq(Dict::getDescs, descs.trim())
        );
        return dict;
    }

    @Override
    @Transactional
    public Dict saveOrUpdate(DictForm form) {
        Dict bean = null;
        String errorMsg = "保存失败，请联系管理员！";
        try {
            Long userId = userInfoService.getByUserId();
            Dict newDict = getByDescsAndPId(new DictCondition(form.getDescs().trim(), form.getParentId()));
            if (form.getId() != null) {
                bean = getById(form.getId());
                if (bean == null) {
                    errorMsg = "id错误，没有查询到对应数据！";
                    throw new BusinessException(BusinessException.BUSSINESS_EXCEPTION, errorMsg);
                }
                if (newDict != null && !newDict.getDescs().equals(bean.getDescs())) {
                    errorMsg = "同级字典的描述不能重复！";
                    throw new BusinessException(BusinessException.BUSSINESS_EXCEPTION, errorMsg);
                }
                bean.setUpdateId(userId);
                bean.setUpdateDate(new Date());
            } else {
                if (newDict != null) {
                    errorMsg = "同级字典的描述不能重复！";
                    throw new BusinessException(BusinessException.BUSSINESS_EXCEPTION, errorMsg);
                }
                bean = new Dict();
                bean.setCreateId(userId);
            }
            SpringUtils.copyPropertiesIgnoreNull(form, bean);
            saveOrUpdate(bean);
        } catch (Exception e) {
            log.error(errorMsg, e);
            throw new BusinessException(BusinessException.BUSSINESS_EXCEPTION, errorMsg);
        }
        return bean;
    }

    @Override
    public void batchExport(DictCondition condition, HttpServletResponse response) {
        //获取全部数据
        condition.setCurrent(-1);
        condition.setOrderBy("type desc,parent_id asc");
        QueryResult<Dict> page = this.page(condition);
        List<DictExport> exportArrayList = Lists.newArrayList();
        String excelName = "数据-" + System.currentTimeMillis() + "-" + IdentitiesUtils.uuid2();
        if (!CollectionUtils.isEmpty(page.getResultData())) {
            for (Dict bean : page.getResultData()) {
                DictExport export = new DictExport();
                SpringUtils.copyPropertiesIgnoreNull(bean, export);
                export.setParentDescs(bean.getParent() != null ? bean.getParent().getDescs() : "");
                export.setCreateDate(DateUtils.formatDateTime(bean.getCreateDate()));
                exportArrayList.add(export);
            }
        }
        ExcelUtils.export2Web(response, excelName, "数据", DictExport.class, exportArrayList);
    }

    @Override
    public void removeJudge(List<Long> idList) {
        for (Long id : idList) {
            QueryWrapper<Dict> wrapper = new QueryWrapper<>();
            wrapper.eq("parent_id", id);
            List<Dict> dataList = list(wrapper);
            if (!CollectionUtils.isEmpty(dataList)) {
                Dict dict = getById(id);
                String message = "请先删除{" + dict.getDescs() + "}的下级字典！";
                throw new BusinessException(BusinessException.BUSSINESS_EXCEPTION, message);
            }
        }
    }

    @Override
    @Transactional
    public void insertTest() {
        List<TableData> tableDataList = Lists.newArrayList();
        TableData tableData = new TableData();
        tableData.setKey("code");
        tableData.setValue("123");

        TableData tableData2 = new TableData();
        tableData2.setKey("descs");
        tableData2.setValue("测试描述值");

        tableDataList.add(tableData);
        tableDataList.add(tableData2);

        TableMap map = new TableMap();
        map.setTableName("dict");
        map.setTableDataList(tableDataList);
        commonMapper.dynamicInsert(map);
    }

    @Override
    @Transactional
    public void updateTest() {
        List<TableData> tableDataList = Lists.newArrayList();
        TableData tableData = new TableData();
        tableData.setKey("code");
        tableData.setValue("456");

        TableData tableData2 = new TableData();
        tableData2.setKey("descs");
        tableData2.setValue("测试描述值1");

        tableDataList.add(tableData);
        tableDataList.add(tableData2);


        List<Long> idList = Lists.newArrayList();
        idList.add(15L);
        idList.add(16L);
        idList.add(17L);

        TableMap map = new TableMap();
        map.setTableName("dict");
        map.setTableDataList(tableDataList);
        map.setIdList(idList);
        commonMapper.dynamicUpdate(map);
    }

    @Override
    public List<TreeDataVo> listTree(DictListTreeCondition condition) {
        List<TreeDataVo> resultList = Lists.newArrayList();
        QueryWrapper<Dict> wrapper = new QueryWrapper<>();
        //根据关键字查询
        if(StringUtils.isNotBlank(condition.getWords())){
            wrapper.like("descs", condition.getWords());
        }
        List<Dict> list = list(wrapper);
        if(StringUtils.isNotBlank(condition.getWords())){
            List<Dict> listAdd = Lists.newArrayList();
            for(Dict bean:list){
                listAdd.addAll(this.getParentsDB(bean.getParentId()));
                listAdd.addAll(this.getChildrenListDB(bean.getId()));
            }
            list.addAll(listAdd);
            list = list.stream().distinct().collect(Collectors.toList());
        }

        if(!CollectionUtils.isEmpty(list)){
            //用parentId进行分组
            Map<Long, List<Dict>> dataMap = list.stream().collect(Collectors.groupingBy(Dict::getParentId));
            //查询 parentId=0 的集合
            List<Dict> topList = getTopList(dataMap);
            if(!CollectionUtils.isEmpty(topList)) {
                for (Dict bean : topList) {
                    TreeDataVo vo = new TreeDataVo(bean.getId(), bean.getDescs(), bean.getLevel());
                    vo.setChildren(this.getChildrenList(bean.getId(), dataMap));
                    resultList.add(vo);
                }
                //用level进行排序(从小到大)
                resultList = resultList.stream().sorted(Comparator.comparingInt(TreeDataVo::getLevel)).collect(Collectors.toList());
            }
        }
        return resultList;
    }

    private List<Dict> getTopList(Map<Long, List<Dict>> dataMap){
        for(Map.Entry<Long, List<Dict>> entry : dataMap.entrySet()){
            if(DictConstant.HIGHEST_CODE == entry.getKey()){
                return entry.getValue();
            }
        }
        return null;
    }

    private List<TreeDataVo> getChildrenList(Long parentId,Map<Long, List<Dict>> dataMap){
        List<TreeDataVo> resultList = Lists.newArrayList();
        for(Map.Entry<Long, List<Dict>> entry : dataMap.entrySet()){
            if(parentId == entry.getKey()){
                for(Dict bean : entry.getValue()){
                    TreeDataVo vo = new TreeDataVo(bean.getId(),bean.getDescs(),bean.getLevel());
                    List<TreeDataVo> childrenList = this.getChildrenList(bean.getId(), dataMap);
                    //用level进行排序(从小到大)
                    childrenList = childrenList.stream().sorted(Comparator.comparingInt(TreeDataVo::getLevel)).collect(Collectors.toList());
                    vo.setChildren(childrenList);
                    resultList.add(vo);
                }
            }
        }
        //用level进行排序(从小到大)
        resultList = resultList.stream().sorted(Comparator.comparingInt(TreeDataVo::getLevel)).collect(Collectors.toList());
        return resultList;
    }

    public List<Dict> getParentsDB(Long id) {
        List<Dict> resultList = Lists.newArrayList();
        Dict parent = getById(id);
        if (parent != null) {
            resultList.add(parent);
            resultList.addAll(this.getParentsDB(parent.getParentId()));
        }
        return resultList;
    }

    public List<Dict> getChildrenListDB(Long id) {
        List<Dict> resultList = Lists.newArrayList();
        QueryWrapper<Dict> wrapper = new QueryWrapper<>();
        wrapper.eq("parent_id", id);
        List<Dict> dataList = list(wrapper);
        if (!CollectionUtils.isEmpty(dataList)) {
            resultList.addAll(dataList);
            for(Dict bean : dataList){
                resultList.addAll(this.getChildrenListDB(bean.getId()));
            }
        }
        return resultList;
    }

}
